#include "HKVideoCapture.hpp"

using namespace std;
using namespace cv;

Mat HKVideoCapture::frame_ = Mat();
bool HKVideoCapture::frame_empty_ = 0;
pthread_mutex_t HKVideoCapture::mutex_;

HKVideoCapture::HKVideoCapture()
{
    handle = NULL;

    //************************* 参数默认值 ********************************/
    width = 1280;
    height = 1024;
    FrameRateEnable = (int)true;
    FrameRate = 210;
    BurstFrameCount = 10; // 一次触发采集的次数
    ExposureTime = 5000;
    GammaEnable = (int)true;
    Gamma = (float)0.7;
    GainAuto = 0;
    Gain = 5;
    SaturationEnable = (int)true;
    Saturation = 128;
    Offset_x = 0;
    Offset_y = 0;
    TriggerMode = 1;
    TriggerSource = 2;
    LineSelector = 2;

    //********** 枚举设备 ********************************/
    MV_CC_DEVICE_INFO_LIST stDeviceList;
    memset(&stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST));
    nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &stDeviceList);
    if (MV_OK != nRet)
    {
        printf("MV_CC_EnumDevices fail! nRet [%x]\n", nRet);
        exit(-1);
    }
    unsigned int nIndex = 0;
    if (stDeviceList.nDeviceNum > 0)
    {
        for (int i = 0; i < stDeviceList.nDeviceNum; i++)
        {
            printf("[device %d]:\n", i);
            MV_CC_DEVICE_INFO *pDeviceInfo = stDeviceList.pDeviceInfo[i];
            if (NULL == pDeviceInfo)
            {
                break;
            }
            PrintDeviceInfo(pDeviceInfo);
        }
    }
    else
    {
        printf("Find No Devices!\n");
        exit(-1);
    }

    //********** 选择设备并创建句柄 *************************/

    nRet = MV_CC_CreateHandle(&handle, stDeviceList.pDeviceInfo[0]);

    if (MV_OK != nRet)
    {
        printf("MV_CC_CreateHandle fail! nRet [%x]\n", nRet);
        exit(-1);
    }

    // 打开设备
    //********** frame **********/

    nRet = MV_CC_OpenDevice(handle);

    if (MV_OK != nRet)
    {
        printf("MV_CC_OpenDevice fail! nRet [%x]\n", nRet);
        exit(-1);
    }

    //设置默认的配置
    this->set(CAP_PROP_FRAMERATE_ENABLE, FrameRateEnable);
    this->set(CAP_PROP_FRAMERATE, FrameRate);
    // this->set(CAP_PROP_BURSTFRAMECOUNT, BurstFrameCount);
    this->set(CAP_PROP_HEIGHT, height);
    this->set(CAP_PROP_WIDTH, width);
    this->set(CAP_PROP_OFFSETX, Offset_x);
    this->set(CAP_PROP_OFFSETY, Offset_y);
    this->set(CAP_PROP_EXPOSURE_TIME, ExposureTime);
    this->set(CAP_PROP_GAMMA_ENABLE, GammaEnable);
    this->set(CAP_PROP_GAMMA, Gamma);
    this->set(CAP_PROP_GAINAUTO, GainAuto);
    this->set(CAP_PROP_GAIN, Gain);
    // this->set(CAP_PROP_TRIGGER_MODE, TriggerMode);
    // this->set(CAP_PROP_TRIGGER_SOURCE, TriggerSource);
    // this->set(CAP_PROP_LINE_SELECTOR, LineSelector);

    //********** frame **********/
    //白平衡 自适应（给定参数1）

    nRet = MV_CC_SetEnumValue(handle, "BalanceWhiteAuto", 2);

//    //白平衡度
//    int rgb[3] = {1290, 1024, 1474};
//    for (int i = 0; i < 3; i++)
//    {
//        //********** frame **********/

//        nRet = MV_CC_SetEnumValue(handle, "BalanceRatioSelector", i);
//        nRet = MV_CC_SetIntValue(handle, "BalanceRatio", rgb[i]);
//    }
    if (MV_OK == nRet)
    {
        printf("set BalanceRatio OK!\n");
    }
    else
    {
        printf("Set BalanceRatio Failed! nRet = [%x]\n\n", nRet);
    }
    this->set(CAP_PROP_SATURATION_ENABLE, SaturationEnable);
    this->set(CAP_PROP_SATURATION, Saturation);
    //软件触发
    // ********** frame **********/
    nRet = MV_CC_SetEnumValue(handle, "TriggerMode", 0);
    if (MV_OK == nRet)
    {
        printf("set TriggerMode OK!\n");
    }
    else
    {
        printf("MV_CC_SetTriggerMode fail! nRet [%x]\n", nRet);
    }

    //********** 图像格式 **********/
    // 0x01100003:Mono10
    // 0x010C0004:Mono10Packed
    // 0x01100005:Mono12
    // 0x010C0006:Mono12Packed
    // 0x01100007:Mono16
    // 0x02180014:RGB8Packed
    // 0x02100032:YUV422_8
    // 0x0210001F:YUV422_8_UYVY
    // 0x01080008:BayerGR8
    // 0x01080009:BayerRG8
    // 0x0108000A:BayerGB8
    // 0x0108000B:BayerBG8
    // 0x0110000e:BayerGB10
    // 0x01100012:BayerGB12
    // 0x010C002C:BayerGB12Packed
    nRet = MV_CC_SetEnumValue(handle, "PixelFormat", 0x02180014); // 目前 RGB

    if (MV_OK == nRet)
    {
        printf("set PixelFormat OK!\n");
    }
    else
    {
        printf("MV_CC_SetPixelFormat fail! nRet [%x]\n", nRet);
    }
    MVCC_ENUMVALUE t = {0};
    //********** frame **********/

    nRet = MV_CC_GetEnumValue(handle, "PixelFormat", &t);

    if (MV_OK == nRet)
    {
        printf("PixelFormat :%d!\n", t.nCurValue); // 35127316
    }
    else
    {
        printf("get PixelFormat fail! nRet [%x]\n", nRet);
    }
    // 开始取流
    //********** frame **********/

    nRet = MV_CC_StartGrabbing(handle);

    if (MV_OK != nRet)
    {
        printf("MV_CC_StartGrabbing fail! nRet [%x]\n", nRet);
        exit(-1);
    }
    //初始化互斥量
    nRet = pthread_mutex_init(&mutex_, NULL);
    if (nRet != 0)
    {
        perror("pthread_create failed\n");
        exit(-1);
    }
    //********** frame **********/

    nRet = pthread_create(&nThreadID, NULL, HKWorkThread, handle);

    if (nRet != 0)
    {
        printf("thread create failed.ret = %d\n", nRet);
        exit(-1);
    }
    else
    {
        printf("thread create succeeded.ret = %d\n", nRet);
    }
}



HKVideoCapture::~HKVideoCapture()
{
    int nRet;
    //********** frame **********/

    pthread_join(nThreadID, NULL);

    //********** frame **********/

    nRet = MV_CC_StopGrabbing(handle);

    if (MV_OK != nRet)
    {
        printf("MV_CC_StopGrabbing fail! nRet [%x]\n", nRet);
        exit(-1);
    }
    printf("MV_CC_StopGrabbing succeed.\n");
    // 关闭设备
    //********** frame **********/

    nRet = MV_CC_CloseDevice(handle);

    if (MV_OK != nRet)
    {
        printf("MV_CC_CloseDevice fail! nRet [%x]\n", nRet);
        exit(-1);
    }
    printf("MV_CC_CloseDevice succeed.\n");
    // 销毁句柄
    //********** frame **********/

    nRet = MV_CC_DestroyHandle(handle);

    if (MV_OK != nRet)
    {
        printf("MV_CC_DestroyHandle fail! nRet [%x]\n", nRet);
        exit(-1);
    }
    printf("MV_CC_DestroyHandle succeed.\n");
    // 销毁互斥量
    pthread_mutex_destroy(&mutex_);
}

bool HKVideoCapture::set(HKVideoCapture::CamerProperties type, float value)
{
    switch (type)
    {
    case CAP_PROP_FRAMERATE_ENABLE:
    {
        //********** frame **********/

        nRet = MV_CC_SetBoolValue(handle, "AcquisitionFrameRateEnable", value);

        if (MV_OK == nRet)
        {
            printf("set AcquisitionFrameRateEnable OK!\n");
        }
        else
        {
            printf("Set AcquisitionFrameRateEnable Failed! nRet = [%x]\n\n", nRet);
        }
        break;
    }
    case CAP_PROP_FRAMERATE:
    {
        //********** frame **********/

        nRet = MV_CC_SetFloatValue(handle, "AcquisitionFrameRate", value);

        if (MV_OK == nRet)
        {
            printf("set AcquisitionFrameRate OK!\n");
        }
        else
        {
            printf("Set AcquisitionFrameRate Failed! nRet = [%x]\n\n", nRet);
        }
        break;
    }
    case CAP_PROP_BURSTFRAMECOUNT:
    {
        //********** frame **********/

        nRet = MV_CC_SetIntValue(handle, "AcquisitionBurstFrameCount", value);

        if (MV_OK == nRet)
        {
            printf("set AcquisitionBurstFrameCount OK!\n");
        }
        else
        {
            printf("Set AcquisitionBurstFrameCount Failed! nRet = [%x]\n\n", nRet);
        }
        break;
    }
    case CAP_PROP_HEIGHT:
    {
        //********** frame **********/

        nRet = MV_CC_SetIntValue(handle, "Height", value); //图像高度

        if (MV_OK == nRet)
        {
            printf("set Height OK!\n");
        }
        else
        {
            printf("Set Height Failed! nRet = [%x]\n\n", nRet);
        }
        break;
    }
    case CAP_PROP_WIDTH:
    {
        //********** frame **********/

        nRet = MV_CC_SetIntValue(handle, "Width", value); //图像宽度

        if (MV_OK == nRet)
        {
            printf("set Width OK!\n");
        }
        else
        {
            printf("Set Width Failed! nRet = [%x]\n\n", nRet);
        }
        break;
    }
    case CAP_PROP_OFFSETX:
    {
        //********** frame **********/

        nRet = MV_CC_SetIntValue(handle, "OffsetX", value); //图像宽度

        if (MV_OK == nRet)
        {
            printf("set Offset X OK!\n");
        }
        else
        {
            printf("Set Offset X Failed! nRet = [%x]\n\n", nRet);
        }
        break;
    }
    case CAP_PROP_OFFSETY:
    {
        //********** frame **********/

        nRet = MV_CC_SetIntValue(handle, "OffsetY", value); //图像宽度

        if (MV_OK == nRet)
        {
            printf("set Offset Y OK!\n");
        }
        else
        {
            printf("Set Offset Y Failed! nRet = [%x]\n\n", nRet);
        }
        break;
    }
    case CAP_PROP_EXPOSURE_TIME:
    {
        //********** frame **********/

        nRet = MV_CC_SetFloatValue(handle, "ExposureTime", value); //曝光时间

        if (MV_OK == nRet)
        {
            printf("set ExposureTime OK!\n");
        }
        else
        {
            printf("Set ExposureTime Failed! nRet = [%x]\n\n", nRet);
        }
        break;
    }
    case CAP_PROP_GAMMA_ENABLE:
    {
        //********** frame **********/

        nRet = MV_CC_SetBoolValue(handle, "GammaEnable", value); //伽马因子是否可调  默认不可调（false）

        if (MV_OK == nRet)
        {
            printf("set GammaEnable OK!\n");
        }
        else
        {
            printf("Set GammaEnable Failed! nRet = [%x]\n\n", nRet);
        }
        break;
    }
    case CAP_PROP_GAMMA:
    {
        //********** frame **********/

        nRet = MV_CC_SetFloatValue(handle, "Gamma", value); //伽马越小 亮度越大

        if (MV_OK == nRet)
        {
            printf("set Gamma OK!\n");
        }
        else
        {
            printf("Set Gamma Failed! nRet = [%x]\n\n", nRet);
        }
        break;
    }
    case CAP_PROP_GAINAUTO:
    {
        //********** frame **********/

        nRet = MV_CC_SetEnumValue(handle, "GainAuto", value); //亮度 越大越亮

        if (MV_OK == nRet)
        {
            printf("set GainAuto OK!\n");
        }
        else
        {
            printf("Set GainAuto Failed! nRet = [%x]\n\n", nRet);
        }
        break;
    }
    case CAP_PROP_GAIN:
    {
        //********** frame **********/

        nRet = MV_CC_SetFloatValue(handle, "Gain", value); //亮度 越大越亮

        if (MV_OK == nRet)
        {
            printf("set Gain OK!\n");
        }
        else
        {
            printf("Set Gain Failed! nRet = [%x]\n\n", nRet);
        }
        break;
    }
    case CAP_PROP_SATURATION_ENABLE:
    {
        //********** frame **********/

        nRet = MV_CC_SetBoolValue(handle, "SaturationEnable", value); //饱和度是否可调 默认不可调(false)

        if (MV_OK == nRet)
        {
            printf("set SaturationEnable OK!\n");
        }
        else
        {
            printf("Set SaturationEnable Failed! nRet = [%x]\n\n", nRet);
        }
        break;
    }
    case CAP_PROP_SATURATION:
    {
        //********** frame **********/

        nRet = MV_CC_SetIntValue(handle, "Saturation", value); //饱和度 默认128 最大255

        if (MV_OK == nRet)
        {
            printf("set Saturation OK!\n");
        }
        else
        {
            printf("Set Saturation Failed! nRet = [%x]\n\n", nRet);
        }
        break;
    }

    case CAP_PROP_TRIGGER_MODE:
    {

        nRet = MV_CC_SetEnumValue(handle, "TriggerMode", value); //饱和度 默认128 最大255

        if (MV_OK == nRet)
        {
            printf("set TriggerMode OK!\n");
        }
        else
        {
            printf("Set TriggerMode Failed! nRet = [%x]\n\n", nRet);
        }
        break;
    }
    case CAP_PROP_TRIGGER_SOURCE:
    {

        nRet = MV_CC_SetEnumValue(handle, "TriggerSource", value); //饱和度 默认128 最大255255

        if (MV_OK == nRet)
        {
            printf("set TriggerSource OK!\n");
        }
        else
        {
            printf("Set TriggerSource Failed! nRet = [%x]\n\n", nRet);
        }
        break;
    }
    case CAP_PROP_LINE_SELECTOR:
    {

        nRet = MV_CC_SetEnumValue(handle, "LineSelector", value); //饱和度 默认128 最大255

        if (MV_OK == nRet)
        {
            printf("set LineSelector OK!\n");
        }
        else
        {
            printf("Set LineSelector Failed! nRet = [%x]\n\n", nRet);
        }
        break;
    }

    default:
        return 0;
    }
    return nRet;
}

bool HKVideoCapture::reset()
{
    nRet = this->set(CAP_PROP_FRAMERATE_ENABLE, FrameRateEnable);
    nRet = this->set(CAP_PROP_FRAMERATE, FrameRate) || nRet;
    // nRet = this->set(CAP_PROP_BURSTFRAMECOUNT, BurstFrameCount) || nRet;
    nRet = this->set(CAP_PROP_HEIGHT, height) || nRet;
    nRet = this->set(CAP_PROP_WIDTH, width) || nRet;
    nRet = this->set(CAP_PROP_OFFSETX, Offset_x) || nRet;
    nRet = this->set(CAP_PROP_OFFSETY, Offset_y) || nRet;
    nRet = this->set(CAP_PROP_EXPOSURE_TIME, ExposureTime) || nRet;
    nRet = this->set(CAP_PROP_GAMMA_ENABLE, GammaEnable) || nRet;
    nRet = this->set(CAP_PROP_GAMMA, Gamma) || nRet;
    nRet = this->set(CAP_PROP_GAINAUTO, GainAuto) || nRet;
    nRet = this->set(CAP_PROP_GAIN, Gain) || nRet;
    nRet = this->set(CAP_PROP_SATURATION_ENABLE, SaturationEnable) || nRet;
    nRet = this->set(CAP_PROP_SATURATION, Saturation) || nRet;
    nRet = this->set(CAP_PROP_TRIGGER_MODE, TriggerMode) || nRet;
    nRet = this->set(CAP_PROP_TRIGGER_SOURCE, TriggerSource) || nRet;
    nRet = this->set(CAP_PROP_LINE_SELECTOR, LineSelector) || nRet;
    return nRet;
}

bool HKVideoCapture::PrintDeviceInfo(MV_CC_DEVICE_INFO *pstMVDevInfo)
{
    if (NULL == pstMVDevInfo)
    {
        printf("%s\n", "The Pointer of pstMVDevInfoList is NULL!");
        return false;
    }
    if (pstMVDevInfo->nTLayerType == MV_GIGE_DEVICE)
    {
        printf("%s %x\n", "nCurrentIp:", pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp);                 //当前IP
        printf("%s %s\n\n", "chUserDefinedName:", pstMVDevInfo->SpecialInfo.stGigEInfo.chUserDefinedName); //用户定义名
    }
    else if (pstMVDevInfo->nTLayerType == MV_USB_DEVICE)
    {
        printf("UserDefinedName:%s\n\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chUserDefinedName);
    }
    else
    {
        printf("Not support.\n");
    }
    return true;
}

void HKVideoCapture::ReadImg(cv::Mat &image)
{
    pthread_mutex_lock(&mutex_);
    if (frame_empty_)
    {
        image = cv::Mat();
    }
    else
    {
        image = frame_.clone();
        frame_empty_ = 1;
    }
    pthread_mutex_unlock(&mutex_);
}

void* HKVideoCapture::HKWorkThread(void* p_handle)
{
    double start;
    int nRet;
    unsigned char *m_pBufForDriver = (unsigned char *)malloc(sizeof(unsigned char) * MAX_IMAGE_DATA_SIZE);
    unsigned char *m_pBufForSaveImage = (unsigned char *)malloc(MAX_IMAGE_DATA_SIZE);
    MV_FRAME_OUT_INFO_EX stImageInfo = {0};
    MV_CC_PIXEL_CONVERT_PARAM stConvertParam = {0};
    cv::Mat tmp;
    int image_empty_count = 0; //空图帧数
    while (1)
    {
        start = static_cast<double>(cv::getTickCount());
        nRet = MV_CC_GetOneFrameTimeout(p_handle, m_pBufForDriver, MAX_IMAGE_DATA_SIZE, &stImageInfo, 15);
        if (nRet != MV_OK)
        {
            if (++image_empty_count > 100)
            {
                cout<<"The Number of Faild Reading Exceed The Set Value!\n";
                exit(-1);
            }
            continue;
        }
        image_empty_count = 0; //空图帧数
        //转换图像格式为BGR8

        stConvertParam.nWidth = 1280;                               //ch:图像宽 | en:image width
        stConvertParam.nHeight = 1024;                              //ch:图像高 | en:image height
        stConvertParam.pSrcData = m_pBufForDriver;                  //ch:输入数据缓存 | en:input data buffer
        stConvertParam.nSrcDataLen = MAX_IMAGE_DATA_SIZE;           //ch:输入数据大小 | en:input data size
        stConvertParam.enDstPixelType = PixelType_Gvsp_BGR8_Packed; //ch:输出像素格式 | en:output pixel format                      //! 输出格式 RGB
        stConvertParam.pDstBuffer = m_pBufForSaveImage;             //ch:输出数据缓存 | en:output data buffer
        stConvertParam.nDstBufferSize = MAX_IMAGE_DATA_SIZE;        //ch:输出缓存大小 | en:output buffer size
        stConvertParam.enSrcPixelType = stImageInfo.enPixelType;    //ch:输入像素格式 | en:input pixel format                       //! 输入格式 RGB
        MV_CC_ConvertPixelType(p_handle, &stConvertParam);


        pthread_mutex_lock(&mutex_);
        frame_ = cv::Mat(stImageInfo.nHeight, stImageInfo.nWidth, CV_8UC3, m_pBufForSaveImage).clone(); //tmp.clone();
        frame_empty_ = 0;
        double time = ((double)cv::getTickCount() - start) / cv::getTickFrequency();
        //*************************************testing img********************************//
        ///std::cout << "HK_camera,Time:" << time << "\tFPS:" << 1 / time << std::endl;
        ///imshow("HK vision",HKVideoCapture::frame_);
        ///waitKey(1);
        pthread_mutex_unlock(&mutex_);

    }
    free(m_pBufForDriver);
    free(m_pBufForSaveImage);
    return 0;
}


